import { BackSide, BufferAttribute, BufferGeometry, ClampToEdgeWrapping, FrontSide, LinearFilter, LinearMipMapLinearFilter, Mesh, MeshBasicMaterial, PlaneGeometry, TextureLoader, Vector3 } from "three";
import Geodetic3D from "../math/Geodetic3D";
import Config from "../Config";
import PriorityQueue from "./PriorityQueue";
import Triangle from "../math/Triangle";
import GeoUtils from "./GeoUtils";

export default class GeometryUtil {

    public static test(geoPointArray: Array<Geodetic3D>, imgUrl) {
        //bug
        // let geodetic3D1 = new Geodetic3D(-39.053168194740216,  57.19900134302458, 0);
        // let geodetic3D2 = new Geodetic3D(28.538850442975832,  74.60140733767864, 0);
        // let geodetic3D3 = new Geodetic3D(-177.140754510538,  74.95707124781174, 0);
        // let geodetic3D4 = new Geodetic3D(-91.74305287583361, 56.838288228250164, 0);

        // let geodetic3D1 = new Geodetic3D(-76.3629827527692, 60.45612501646955, 0);
        // let geodetic3D2 = new Geodetic3D(-21.878715961133285, 63.28487173327713, 0);
        // let geodetic3D3 = new Geodetic3D(98.63041883600722, 87.25634880470426, 0);
        // let geodetic3D4 = new Geodetic3D(-125.14159020420477, 70.41372239474993, 0);

        // let geodetic3D1 = new Geodetic3D(162.08500127026898, 52.71705744320837, 0);
        // let geodetic3D2 = new Geodetic3D(170.5030351643348, 17.53012084325477, 0);
        // let geodetic3D3 = new Geodetic3D(-132.13282457400143, 9.293495146470338, 0);
        // let geodetic3D4 = new Geodetic3D(-119.47077685808141, 56.79773675707354, 0);

        // let geodetic3D1 = new Geodetic3D(74.94749291502359, 7.422161365854796, 0);
        // let geodetic3D2 = new Geodetic3D(76.2338881978793, -10.804365787259671, 0);
        // let geodetic3D3 = new Geodetic3D(113.3098654795894, -10.700638953986882, 0);
        // let geodetic3D4 = new Geodetic3D(107.23528416776168, 9.45225208549007, 0);

        // let geodetic3D4 = new Geodetic3D(104.17003810235585, 33.82170905136536, 0);
        // let geodetic3D3 = new Geodetic3D(108.811541540741, 18.644175412031718, 0);
        // let geodetic3D2 = new Geodetic3D(121.00323527615863, 28.741194657565014, 0);
        // let geodetic3D1 = new Geodetic3D(117.41457884974575, 39.38296342991897, 0);

        // let geodetic3D1 = new Geodetic3D(11.381582182994173, 7.422161365854796, 0);
        // let geodetic3D2 = new Geodetic3D(13.092167113471397, -10.804365787259671, 0);
        // let geodetic3D3 = new Geodetic3D(28.44143677019041, -10.700638953986882, 0);
        // let geodetic3D4 = new Geodetic3D(29.054682402449448, 9.45225208549007, 0);

        // let geodetic3D1 = new Geodetic3D(117.41457884974575, 7.422161365854796, 0);
        // let geodetic3D2 = new Geodetic3D(121.00323527615863, -10.804365787259671, 0);
        // let geodetic3D3 = new Geodetic3D(108.811541540741, -10.700638953986882, 0);
        // let geodetic3D4 = new Geodetic3D(104.17003810235585, 9.45225208549007, 0);        
        
        // let geodetic3D1 = new Geodetic3D(104.00127439753727, -12.625465075703461, 3000000);
        // let geodetic3D2 = new Geodetic3D(106.11186221110472, -29.918616838596034, 3000000);
        // let geodetic3D3 = new Geodetic3D(126.36254480147404, -28.477546407082016, 3000000);
        // let geodetic3D4 = new Geodetic3D(122.6252859821423, -10.214484787389505, 3000000);    

        // let geodetic3D1 = new Geodetic3D(-22.035780342417493,  25.652824917730207, 1000000);
        // let geodetic3D2 = new Geodetic3D(-15.036761789255902,  -22.9198195300547, 1000000);
        // let geodetic3D3 = new Geodetic3D(54.77487200337165,  -21.14417009199964, 1000000);
        // let geodetic3D4 = new Geodetic3D(46.06099298778647,  30.224488632545512, 1000000);    

        let ellipsoid = Config.Ellipsoid
        // let ecefPoint1 = ellipsoid.toEcef(geodetic3D1);
        // let ecefPoint2 = ellipsoid.toEcef(geodetic3D2);
        // let ecefPoint3 = ellipsoid.toEcef(geodetic3D3);
        // let ecefPoint4 = ellipsoid.toEcef(geodetic3D4);

        let ecefPointArray = []; //[ecefPoint1, ecefPoint2, ecefPoint3, ecefPoint4];
        geoPointArray.forEach((element, index) => {
            let ecefPoint = ellipsoid.toEcef(element);
            ecefPointArray.push(ecefPoint);
        });
        let earClipData = GeometryUtil.generateSurfaceOnEllipooid(ecefPointArray);
        console.log(earClipData)

        // let imgUrl = '/examples/resources/image/beauty.jpg';
        let material = new MeshBasicMaterial({
            // map: new TextureLoader().load(imgUrl, (texture) => {
            //     texture.minFilter = LinearMipMapLinearFilter; // 使用带mipmap的过滤方式
            //     texture.magFilter = LinearFilter;
            //     texture.wrapS = RepeatWrapping;
            //     texture.wrapT = RepeatWrapping;
            //     texture.generateMipmaps = true; // 启用mipmap生成
            //     // texture.flipY = false;
            //     texture.colorSpace = 'srgb';
            //     material.needsUpdate = true;
            // }),
            // wireframe: true, // 这会导致只显示线框
            color: '#FFFFFF',
            transparent: false,
            opacity: 1,
            side: earClipData.isCCW ? FrontSide : BackSide,
            depthTest: true,
            colorWrite: true,
            
        });
        if(imgUrl){
            material.map = new TextureLoader().load(imgUrl, (texture) => {
                texture.minFilter = LinearMipMapLinearFilter; // 使用带mipmap的过滤方式
                texture.magFilter = LinearFilter;
                texture.wrapS = ClampToEdgeWrapping;
                texture.wrapT = ClampToEdgeWrapping;
                texture.generateMipmaps = true; // 启用mipmap生成
                // texture.flipY = false;
                texture.colorSpace = 'srgb';
                material.needsUpdate = true;
            });
        }else{
            material.wireframe = true;
        }

        let geometry = new BufferGeometry();
        const vertices = new Float32Array(earClipData.pointArray);
        const uvArray = new Float32Array(earClipData.uvCoordArray);
        geometry.setAttribute('position', new BufferAttribute(vertices, 3));
        geometry.setAttribute('uv', new BufferAttribute(uvArray, 2));
        geometry.computeVertexNormals();

        let mesh = new Mesh(geometry, material);
        mesh.position.set(earClipData.firstPoint.x, earClipData.firstPoint.y, earClipData.firstPoint.z);
        // mesh.scale.set(1.01, 1.01, 1.01);
        // let geoPoint = ellipsoid.toGeography(earClipData.firstPoint);
        return mesh;
    }

    public static subdivisionTriangle(priorityQueue: PriorityQueue<Triangle>, maxEdgeLength: nmuber) {
        let currentTriangle = priorityQueue.peek();
        while (currentTriangle) {
            if (currentTriangle.getMaxEdge().edgeLength >= maxEdgeLength) {
                let topTriangle = priorityQueue.pop();
                let tirangleArray = topTriangle.divide();
                tirangleArray.forEach(triangle => {
                    priorityQueue.add(triangle, triangle.getMaxEdge().edgeLength);
                });

                currentTriangle = priorityQueue.peek();
            } else {
                break;
            }
        }
    }

    public static earClipping(points: Array<Vector3>, maxEdgeLength: nmuber): { pointArray: Array<number>, priorityQueue: PriorityQueue<Triangle>, isCCW: boolean, firstPoint: Vector3 } {
        let ellipsoidCenter = Config.Ellipsoid.center.clone();
        let bPolygonIsConvex: Array<boolean> = new Array < boolean > ();//对所有polygon点信息，凹凸信息进行标定 
        let finalDirection = 0;
        let priorityQueue = new PriorityQueue < Triangle > ();

        for (let i = 0; i < points.length; ++i) {//判断顶点是顺时针还是逆时针
            let curPos = points[i];

            let leftPos: Vector3;
            let rightPos: Vector3;

            if (i == 0) {
                let iEnd = points.length;
                leftPos = points[iEnd - 1];
            } else {
                leftPos = points[i - 1];
            }

            if (i == (points.length - 1)) {
                rightPos = points[0];
            } else {
                rightPos = points[i + 1];
            }

            let vectorOne: Vector3 = curPos.clone().sub(leftPos);
            let vectorTwo: Vector3 = rightPos.clone().sub(curPos);

            //向量叉乘
            let u: Vector3 = vectorOne.cross(vectorTwo).normalize();
            let v: Vector3 = curPos.clone().sub(ellipsoidCenter);
            let dotValue = u.dot(v);

            if (dotValue > 0) {
                finalDirection += 1;
                bPolygonIsConvex.push(true);
            } else if (dotValue < 0) {
                finalDirection -= 1;
                bPolygonIsConvex.push(false);
            } else if (dotValue == 0) {
                bPolygonIsConvex.push(false);
            }
        }

        let isCCW = (finalDirection > 0) ? true : false;
        let returnData: {priorityQueue: PriorityQueue<Triangle>, isCCW: boolean, firstPoint: Vector3 } =
            {priorityQueue: null, isCCW: isCCW, firstPoint: null };
        let firstPoint: Vector3;

        let loopIndex = 0;

        if (points.length < 3) {
            return returnData;
        } else {
            let earFound = true;
            let lastSize = 0;

            while ((points.length >= 3)) {
                if ((points.length == lastSize)) {
                    finalDirection = finalDirection * -1;
                }
                lastSize = points.length;
                if (!earFound) {
                    return returnData;
                }

                for (let i = 0; i < points.length; i++) {
                    let curPos: Vector3 = points[i];
                    let leftPos: Vector3;
                    let rightPos: Vector3;
                    if (i == 0) {
                        let iEnd = points.length;
                        leftPos = points[iEnd - 1];

                        if(loopIndex == 0){
                            firstPoint = curPos.clone();
                        }
                    } else {
                        leftPos = points[i - 1];
                    }
                    if (i == points.length - 1) {
                        rightPos = points[0];
                    } else {
                        rightPos = points[i + 1];
                    }

                    let isEar = true;
                    let isConvex = true;

                    let vectorOne: Vector3 = curPos.clone().sub(leftPos);
                    let vectorTwo: Vector3 = rightPos.clone().sub(curPos);
                    let currentNormal = vectorOne.cross(vectorTwo).normalize();
                    let currentDotValue = currentNormal.dot(curPos.clone().sub(ellipsoidCenter));

                    if (finalDirection > 0) { //逆时针 
                        if (currentDotValue < 0) {
                            //法线往下，就说明这是一个凹点
                            isConvex = false;
                        }
                    }
                    else if (finalDirection < 0) { //顺时针
                        if (currentDotValue > 0) {
                            //法线往上，就说明这是一个凹点
                            isConvex = false;
                        }
                    }

                    if (isConvex == true) {
                        let currentTriangle: Array<Vector3> = new Array < Vector3 > ();//当前的三角形
                        currentTriangle.push(leftPos);
                        currentTriangle.push(curPos);
                        currentTriangle.push(rightPos);
                        currentTriangle.push(leftPos);

                        for (let j = 0; j < points.length; j++) { //检查其他的点是否在这个凸起的三角形的内部
                            if (!isEar) {
                                break;
                            }
                            let currPoint: Vector3 = points[j].clone();
                            let isCheck = true;
                            for (let m = 0; m < currentTriangle.length; m++) { //太近的顶点不参与耳部检查
                                let currTriganglePoint: Vector3 = currentTriangle[m];
                                let distance = currPoint.distanceTo(currTriganglePoint);
                                if (distance <= 0.01) {
                                    isCheck = false;
                                    break;
                                }
                            }

                            if (isCheck == true) {
                                let sum = 1;
                                let currPointDirection = currPoint.clone().sub(ellipsoidCenter);
                                for (let k = 0; k < currentTriangle.length - 1; k++) {
                                    let vectorOne: Vector3 = currentTriangle[k].clone().sub(ellipsoidCenter);
                                    let vectorTwo: Vector3 = currentTriangle[k + 1].sub(ellipsoidCenter);
                                    let normal = vectorOne.cross(vectorTwo);
                                    let dot = currPointDirection.dot(normal);
                                    if ((isCCW && dot < 0) || (!isCCW && dot > 0)) {
                                        sum++;
                                    }
                                }

                                if (sum < 3) { //没有被三角形包含
                                    isEar = true;
                                } else if (sum >= 3) {//被三角形包含
                                    isEar = false;
                                    break;
                                }
                            }
                        }
                    } else {
                        isEar = false;
                        if (points.length == 3) {
                            earFound = false;
                        }
                    }

                    if (isEar && points.length >= 3) {

                        let trianglePointArray = [];
                        trianglePointArray.push(leftPos.clone());
                        trianglePointArray.push(curPos.clone());
                        trianglePointArray.push(rightPos.clone());
                        let triangle = new Triangle(trianglePointArray, maxEdgeLength);
                        priorityQueue.add(triangle, triangle.getMaxEdge().edgeLength);

                        let bufferArray: Array<Vector3> = new Array < Vector3 > ();
                        for (let n = 0; n < points.length; n++) { //把数组复制到新的数组中，为了不破坏原始数据
                            if (n != i) {
                                bufferArray.push(points[n]);
                            }
                        }
                        points = bufferArray;
                        i = i - 1;
                    }

                    loopIndex++;
                }
            }
        }

        returnData.priorityQueue = priorityQueue;
        returnData.firstPoint = firstPoint;
        return returnData;
    }

    public static generateSurfaceOnEllipooid(points: Array<Vector3>) {
        let maxDistance = 0;
        for(let i = 0; i < points.length; i++){
            let outPoint = points[i];
            for(let j=0; j<points.length; j++){
                if(i == j){
                    continue;
                }

                let interPoint = points[j];
                let distance = outPoint.distanceTo(interPoint);
                maxDistance = Math.max(distance, maxDistance);
            }
        }
        
        let maxEdgeLength: nmuber = maxDistance / 20;
        const maxLength = 500000;
        const minLength = 1000;
        if(maxEdgeLength > maxLength){
            maxEdgeLength = maxLength;
        }else if(maxEdgeLength < minLength){
            maxEdgeLength = minLength;
        }
        let earClipData: { priorityQueue: PriorityQueue<Triangle>, isCCW: boolean, firstPoint: Vector3 } = GeometryUtil.earClipping(points, maxEdgeLength);
        GeometryUtil.subdivisionTriangle(earClipData.priorityQueue, maxEdgeLength);

        let ecefPointArray: Array<number> = new Array < number > ();
       
        let originPointOnSurace = Config.Ellipsoid.scaleToGeodeticSurface(earClipData.firstPoint);
        let longitudeArray:Array<number> = new Array<number>();
        let latitudeArray:Array<number> = new Array<number>();

        while (!earClipData.priorityQueue.isEmpty()) {
            let topTriangle = earClipData.priorityQueue.pop();
            let tempPointArray = topTriangle.getPointArray();
            tempPointArray.forEach((point, index) => {
                let newPoint = Config.Ellipsoid.scaleToGeodeticSurface(point);

                let lnglatPoint: Geodetic3D = Config.Ellipsoid.toGeography(newPoint);
                longitudeArray.push(lnglatPoint.longitude);
                latitudeArray.push(lnglatPoint.latitude);

                newPoint = newPoint.sub(originPointOnSurace);
                ecefPointArray.push(newPoint.x, newPoint.y, newPoint.z);
            });
        }
       let uvCoordArray = GeometryUtil.computeSurfaceCoord(longitudeArray, latitudeArray);
       return { pointArray: ecefPointArray, uvCoordArray: uvCoordArray, isCCW: earClipData.isCCW, firstPoint: earClipData.firstPoint };
    }

    private static computeSurfaceCoord(longitudeArray, latitudeArray){
        let minLongitude, minLatitude, maxLongitude, maxLatitude;

        minLongitude = Math.min(...longitudeArray);
        minLatitude = Math.min(...latitudeArray);
        maxLongitude = Math.max(...longitudeArray);
        maxLatitude = Math.max(...latitudeArray);

        let longitudeWidth = maxLongitude - minLongitude;
        let uvCoordArray = [];

        if(longitudeWidth > 180){
            longitudeArray.forEach((longitude, index) => {
                longitudeArray[index] = (longitude < 0) ? longitude + 360 : longitude;
            });

            minLongitude = Math.min(...longitudeArray);
            maxLongitude = Math.max(...longitudeArray);
            longitudeWidth = maxLongitude - minLongitude;
        }
        let latitudeHeight = maxLatitude - minLatitude;
        longitudeArray.forEach((longitude, index) => {
            let u = (longitude - minLongitude) / longitudeWidth;

            let latitude = latitudeArray[index];
            let v = (latitude - minLatitude) / latitudeHeight;
            uvCoordArray.push(u, v);
        });

        return uvCoordArray;
    }

    
    /**计算瓦片在经纬度中的四个点坐标 */
    public static computeTilePoints(tileX: number, tileY: number, tileZ: number): { leftTopPoint: Geodetic3D, leftDownPoint: Geodetic3D, rightTopPoint: Geodetic3D, rightDownPoint: Geodetic3D } {
        let tileHalfWidth = GeoUtils.getTileWidth(tileZ) / 2;
        let xCenter = GeoUtils.getCDevX(tileX, tileZ, 0.5);
        let yCenter = GeoUtils.getRDevY(tileY, tileZ, 0.5);

        let leftTopPointWebMercato = new Vector3(xCenter - tileHalfWidth, yCenter - tileHalfWidth, 0);
        let leftDownPointWebMercato = new Vector3(xCenter - tileHalfWidth, yCenter + tileHalfWidth, 0);

        let rightTopPointWebMercato = new Vector3(xCenter + tileHalfWidth, yCenter - tileHalfWidth, 0);
        let rightDownPointWebMercato = new Vector3(xCenter + tileHalfWidth, yCenter + tileHalfWidth, 0);

        let leftTopPointLnglat = GeoUtils.WebtoP84(leftTopPointWebMercato.x, leftTopPointWebMercato.y);
        
        let leftTopGeodetic = new Geodetic3D(leftTopPointLnglat.x, leftTopPointLnglat.y, 0);

        let leftDownPointLnglat = GeoUtils.WebtoP84(leftDownPointWebMercato.x, leftDownPointWebMercato.y);
        let leftDwonGeodetic = new Geodetic3D(leftDownPointLnglat.x, leftDownPointLnglat.y, 0);

        let rightTopPointLnglat = GeoUtils.WebtoP84(rightTopPointWebMercato.x, rightTopPointWebMercato.y);
        let rightTopGeodetic = new Geodetic3D(rightTopPointLnglat.x, rightTopPointLnglat.y, 0);

        let rightDownPointLnglat = GeoUtils.WebtoP84(rightDownPointWebMercato.x, rightDownPointWebMercato.y);
        let rightDownGeodetic = new Geodetic3D(rightDownPointLnglat.x, rightDownPointLnglat.y, 0);

        return { leftTopPoint: leftTopGeodetic, leftDownPoint: leftDwonGeodetic, rightTopPoint: rightTopGeodetic, rightDownPoint: rightDownGeodetic };
    }
}